package controllers

import (
	"encoding/json"
	"fmt"
	"github.com/astaxie/beego"
	"github.com/astaxie/beego/orm"
	"github.com/gorilla/websocket"
	"neecola.com/learngo/family_security/models"
	"regexp"
	"time"
)

const (
	APP_NOW_DETAIL    = 10001 // app想要获取最新的硬件信息
	APP_RES_WARN      = 10002 // app响应硬件发送过来的异常信息
	PR_RES_NOW_DETAIL = 20001 // 上位机机响应app想要获取最新的硬件信息
	PR_2_APP_WARN     = 20002 // 上位机发送异常的数据信息
)

type UserAction struct {
	beego.Controller
}

func test_append_message_2_pr() {
	go func() {
		message := ProxyMessage{}
		for {
			message.Code = 10001
			message.Phone = "13123456789"
			message.Mac = "00124B001795E329"
			chApp2PrMessage <- message
			time.Sleep(time.Second * 6)

		}
	}()
}
func init() {

	// 测试发送
	//test_append_message_2_pr()
	// 启动 上位机消息到 app 端 的线程信息分发
	go func() {
		fmt.Println("启动上位机的端口分发...")
		for {
			// 获取上位机要发送给app的数据信息
			// 这里 要对 appClients 进行操作
			msg := <-chPr2AppMessage
			fmt.Printf("pr => app msg %+v\n", msg)
			//fmt.Printf(" 上位机信息 msg =%+v \n", msg)
			conn, ok := appClients[msg.Phone]

			if !ok {
				fmt.Println("cannot find the app connect global is ", msg.Phone)
				continue
			}

			// 向指定的app conn 进行写数据
			err := conn.WriteJSON(msg)

			if err != nil {
				fmt.Printf("conn write error %+v\n", err)
				// 关闭连接
				conn.Close()
				//移除异常的socket
				delete(appClients, msg.Phone)
				// 设置app 为不在线状态
				if info := myUserInfoRedis[msg.Phone]; info != nil {
					info.AppOn = false
				}
			} else {
				fmt.Println("向app 写入数据成功")
			}

		}
	}()

	// 启动 app端-> pr 端 的信息分发
	//
	go func() {
		fmt.Println("启动app端的 消息分发...... ")
		Message := struct {
			Code int    `json:"code"`
			Mac  string `json:"mac"`
		}{}
		for {
			msg := <-chApp2PrMessage
			fmt.Printf("app2PrMessage %+v \n", msg)
			// 获取连接的 句柄
			conn, ok := proxyClients[msg.Mac]
			if !ok {
				continue
			}
			msg.Phone = ""
			msg.Id = 0
			//msg.Msg = "" // 将这个信息的内容值为空
			Message.Mac = msg.Mac
			Message.Code = msg.Code

			json.Unmarshal([]byte(msg.Msg), &Message)
			err := conn.WriteJSON(Message)

			// 向 proxy 端写数据遇到错误
			if err != nil {
				fmt.Printf("conn.WriteJSON(msg.Msg) error %+v \n", err)
				// 关闭 conn
				conn.Close()
				// 从map表中删除conn , key is msg.Mac
				delete(proxyClients, msg.Mac)

				info := getUserInfoByMac(msg.Mac)

				if info != nil {
					// 将在线状态置为false
					info.ProxyOn = false
				}
			}
			fmt.Println("app 信息 msg = ", msg)
		}
	}()

	// app 请求, 但是 但是通信目标没有响(上位机)应
	go func() {

		for {
			msg := <-chAppMissPrMessage
			fmt.Printf("上位机没有响应............%+v\n", msg)
			conn := appClients[msg.Phone]
			msg.Msg = `{}`
			msg.Code = 404

			conn.WriteJSON(msg)
		}
	}()

	// 读取全部数据库的全部用户信息
	// 初始化自定义的redis
	// 缓存全部的 用户信息
	go func() {
		//var users []*User
		var users []*models.UserInfo
		// 递归查找....
		num, err := orm.NewOrm().QueryTable("UserInfo").All(&users)

		if err != nil {
			fmt.Println(" all error ", err)
		} else {
			fmt.Println("num = ", num)
			or := orm.NewOrm()
			for i := int64(0); i < num; i++ {
				myUserInfoRedis[users[i].Phone] = users[i]
				or.LoadRelated(users[i], "SubMac")
				fmt.Printf("user = %+v \n", *users[i])
			}
		}
	}()

	// 定时移除 多余的缓存ip
	go clean_access()

	go dispatchAddSubMac()

}

// 分发添加子结点的消息
func dispatchAddSubMac() {
	for {

		msg := <-chAddSubMac

		fmt.Printf("收到添加结点的消息")
		conn, ok := appClients[msg.Phone]

		if !ok {
			fmt.Println("cannot find the app connect global is ", msg.Phone)
			continue
		}

		// 向指定的app conn 进行写数据
		err := conn.WriteJSON(msg)

		if err != nil {
			fmt.Printf("conn write error %+v\n", err)
			// 关闭连接
			conn.Close()
			//移除异常的socket
			delete(appClients, msg.Phone)
			// 设置app 为不在线状态
			if info := myUserInfoRedis[msg.Phone]; info != nil {
				info.AppOn = false
			}
		} else {
			fmt.Println("向app 写入数据成功")
		}

	}
}

var (
	// app端
	appClients = make(map[string]*websocket.Conn)
	// 上位机部分的处理
	proxyClients = make(map[string]*websocket.Conn)
	upgrade      = websocket.Upgrader{}

	// app 端发送给上位机信息
	chApp2PrMessage = make(chan ProxyMessage, 10)

	// 上位机端发送到app的数据
	chPr2AppMessage = make(chan ProxyMessage, 10)

	chAppMissPrMessage = make(chan ProxyMessage, 10)

	// 自定义的redis
	// value 端使用指针 用作修改用户的在在线信息
	myUserInfoRedis = make(map[string]*models.UserInfo)

	// add sub mac
	// 管理员添加新的结点
	chAddSubMac = make(chan ProxyMessage, 10)

	access_t = make(map[string]time.Time)
)

// 定时一个小时移除多余的缓存 ip 地址
func clean_access() {

	for {
		t := time.Now()

		for k, v := range access_t {
			if t.Sub(v) >= time.Second*30 {
				delete(access_t, k)
				fmt.Println("移除  ", k)
			}
		}

		// 线程睡眠
		time.Sleep(time.Second * 30)
	}
}

// 通过 遍历 myUserInfoRedis 容器 获取用户的信息
// 没有获取用户的信息返回 nil
func getUserInfoByMac(mac string) *models.UserInfo {
	var info *models.UserInfo
	for _, v := range myUserInfoRedis {

		if v.Mac == mac {
			//找到匹配的mac地址
			info = v
			break
		}
	}

	return info
}

func (this *UserAction) Prepare() {
	//path := this.Ctx.Request.URL.Path
	fmt.Println("header message whoareu", this.Ctx.Request.Header.Get("whoareu"))
	if who := this.Ctx.Request.Header.Get("whoareu"); who == "" {
		fmt.Println("forbidden")
		this.Abort("403")
	}
}

// 采用json接发数据
func (this *UserAction) WsAppConnect() {
	fmt.Println("WsAppConnect")
	// get raw header
	whoareu := this.Ctx.Request.Header.Get("whoareu")
	// 如果 该字段为空,或者根本不存在
	if whoareu == "" {
		this.byebye("bye bye")
		return
	}
	var message = Message{}
	phone := this.GetString("phone", "")
	password := this.GetString("password", "")

	// 手机号码不正确
	if len(phone) != 11 {
		this.byebye("bye bye ")
		return
	}

	info, ok := myUserInfoRedis[phone]

	if !ok {
		this.byebye("bye bye ")
		return
	}

	// 用户密码 和 myRedis 中的数据不匹配
	if info.PassWord != password {
		this.byebye("bye bye ")
		return
	}

	ul := models.UserLog{} // 用户日志
	ul.UserInfo = info
	ul.IP = this.Ctx.Request.RemoteAddr
	ul.Type = 0
	ul.OPTime = time.Now()
	ul.Save() // 保存日志
	conn, err := upgrade.Upgrade(this.Ctx.ResponseWriter, this.Ctx.Request, nil)

	if err != nil {
		fmt.Printf("error %+v\n", err.Error())

		message.Code = 400
		message.Msg = err.Error()
		this.Data["JSON"] = message
		this.ServeJSON(true)
		return
	}
	// 将conn 添加到 appClients
	// key is phone
	// value is conn
	appClients[phone] = conn

	var proxyMessage = ProxyMessage{}

	// 设置app 客户端的在线信息为true
	info.AppOn = true

	code := struct {
		Code int `json:"code"`
	}{}
	for {
		//{"code":10001,"id":23,"msg":"test","type":10001}
		// 数据格式
		_, data, err := conn.ReadMessage()

		// 读取app客户端的数据遇到错误
		// 关闭 连接
		// 删除 appClients 的数据
		// 返回数据?
		if err != nil {
			fmt.Printf("WsAppConnect ReadMessage  but error %+v \n", err)
			conn.Close()
			delete(appClients, phone)
			info.AppOn = false

			this.byebye("see u ")

			ul.Msg = err.Error()
			ul.ID = 0
			ul.Type = -1 // 类型是一表示错误
			ul.Save()    // 记录错误信息
			return
		}

		// 检测到发送的时心跳包
		if string(data) == `{}` {
			fmt.Println(`WsAppConnect ReadMessage de .5 心跳包`)
			continue
		}
		err = json.Unmarshal(data, &code)

		if err != nil {
			fmt.Printf("WsAppConnect json.Unmarsha  but error %+v \n", err)
			conn.Close()
			delete(appClients, phone)
			info.AppOn = false

			this.byebye("see u ")
			return
		}

		proxyMessage.Msg = string(data) // 设置信息的内容
		proxyMessage.Phone = phone      //发送该数据的app 编号
		proxyMessage.Mac = info.Mac     // 地址编号
		proxyMessage.Code = code.Code   // 设置请求代码

		fmt.Printf("WsAppConnect ReadMessage %+v message =%+v\n", string(data), proxyMessage)
		chApp2PrMessage <- proxyMessage

	}

}

// 上位机 ws 处理
func (this *UserAction) WsPrConnect() {

	conn, err := upgrade.Upgrade(this.Ctx.ResponseWriter, this.Ctx.Request, nil)

	if err != nil {
		this.byebye("bye bye ")
		return
	}
	fmt.Printf("header %+v\n", this.Ctx.Request.Header)

	mac := this.Ctx.Request.Header.Get("mac")

	// mac 为空
	// mac的长度 小于16
	if mac == "" || len(mac) < 16 {
		this.byebye("bye bye")
		return
	}

	// 从缓存中查找用户的信息
	info := getUserInfoByMac(mac)

	// 和该用户的无关的信息 bye bye
	if info == nil {
		this.byebye("bye bye")
		return
	}

	// 将 ws about Proxy的socket添加到 proxyClients
	proxyClients[info.Mac] = conn

	// 将上位机的在线信息设置为 当前在线
	info.ProxyOn = true

	var proxyMessage = ProxyMessage{}
	// 上位机发送的数据格式,
	// 要使用的数据只有 gas ,hum , illegal subNum , tem
	//{"gas":0,"hum":65,"illegal":0,"macAddr":"","subNum":0,"tem":27}

	for {

		_, data, err := conn.ReadMessage()
		// 如果读取数据的时候遇到异常, 关闭 conn
		// 从内存的map 中删除 conn
		// 将上位机的在线状态值为 false
		// 退出
		if err != nil {
			fmt.Printf("WsPrConnect ReadMessage  but error %+v \n", err)
			conn.Close()
			delete(proxyClients, info.Mac)
			info.ProxyOn = false

			this.byebye("see u ")
			return
		}

		proxyMessage.Phone = info.Phone
		proxyMessage.Msg = string(data)
		proxyMessage.Mac = mac
		fmt.Printf("WsPrConnect ReadMessage data = %+v \n", proxyMessage)
		chPr2AppMessage <- proxyMessage

	}

	this.Data["json"] = struct {
		Message string `json:"message"`
	}{
		Message: "bye bye",
	}
	this.ServeJSON(true)
}
func (this *UserAction) Login() {
	message := Message{}
	phone := this.GetString("phone", "")
	password := this.GetString("password", "")

	ok, _ := regexp.MatchString(`^(1[3|4|5|8][0-9]\d{4,8})$`, phone)
	fmt.Println("reg   ccccc  ", ok)
	// 手机号码 不正确 搞出个 404 谢谢
	// 密码
	if phone == "" || password == "" || len(phone) != 11 || !ok {
		this.Abort("404")
		return
	}

	//user := models.UserInfo{}
	//user.Phone = phone
	//user.PassWord = password
	//
	//err := user.Read("phone", "pass_word")

	// 查表....
	user, ok := myUserInfoRedis[phone]
	if !ok || user.PassWord != password {
		message.Msg = "phone or pass wd is incorrect"
		message.Code = 400
		this.Data["json"] = message
		fmt.Printf("ret =%+v\n", message)

		//this.ServeJSON(true)
		this.Abort("404")
		return
	}

	type U struct {
		Mac    string          `json:"mac"`
		SubMac []models.SubMac `json:"sub_mac"`
	}
	u := U{}
	u.Mac = user.Mac

	// 获取 通信信息
	for _, v := range user.SubMac {
		u.SubMac = append(u.SubMac, *v)
	}
	message.Code = 200
	this.Data["json"] = u
	this.ServeJSON(true)
	fmt.Printf("sl =%+v\n", u)
	return
}

// 获取最近几天的简要信息
func (this *UserAction) PickPass() {

	message := Message{}
	phone := this.GetString("phone", "")
	password := this.GetString("password", "")

	if phone == "" || password == "" {
		message.Code = 400
		message.Msg = "can not push nil info to server"
		this.Data["json"] = message
		this.ServeJSON(true)
		return
	}

	user := models.UserInfo{}
	user.Phone = phone
	user.PassWord = password

	err := user.Read("phone", "pass_word")

	if err != nil {
		message.Msg = "phone or pass wd is incorrect"
		message.Code = 400
		this.Data["json"] = message

		this.ServeJSON(true)
		return
	}

	list := models.PickPass_t(&user)
	for v := range list {
		fmt.Printf("v = %+v\n", list[v])

	}
	//bytes, _ := json.Marshal(list)
	//	message.Msg = string(bytes)
	message.Secinfo = list
	message.Code = 200
	this.Data["json"] = message
	this.ServeJSON(true)
	// 保存用户的登录日志
	ul := models.UserLog{}
	ul.Type = 0
	ul.OPTime = time.Now()
	ul.UserInfo = &user
	ul.Msg = "login"
	go func() {
		ul.Save()
	}()

	return
}

func (this *UserAction) AppWsTest() {
	fmt.Println("appWstest")
	this.TplName = "appws.html"
}

func (this *UserAction) PrWsTest() {
	this.TplName = "prws.html"
}

// 获取当前的最新数据
func (this *UserAction) GetNowDetail() {

}

type Message struct {
	Code int    `json:"code"`
	Msg  string `json:"msg"`

	Secinfo       []*models.SecInfo `json:"secinfo"`
	ThirdSecInfo  []*models.SecInfo `json:"third_sec_info"`
	SecondSecInfo []*models.SecInfo `json:"second_sec_info"`
}

// 中转信息
type ProxyMessage struct {
	Id     int64  `json:"id"`
	Code   int    `json:"code"`
	Msg    string `json:"msg"`
	Type   int    `json:"type"` // 数据类型
	Phone  string `json:"-"`
	SubNum int    `json:"subNum"` // 增加发送结点的编号
	Mac    string `json:"-"`      // 对应的硬件编号
}

// bye bye 可能访问的过程中 遇到了什么问题
func (this *UserAction) byebye(message string) {

	this.Data["json"] = struct {
		Message string `json:"message"`
	}{
		Message: message,
	}
	this.ServeJSON(true)
}
